#include "VTTImage.h"

#include "VTTCommon.h"
#include "VTTErrorManager.h"
#include "VTTMemoryManager.h"
#include "VTTPackNode.h"
#include "VTTTimer.h"

#include <new.h>

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#if defined (_MSC_VER)
#define JPEG_INTERNAL_OPTIONS // Gives access to some defines (RGB_PIXELSIZE...)
#define XMD_H // Avoid redefinition of some typedefs (INT16, INT32). They are defined in BaseTsd.h
#endif
#ifdef FAR
#undef FAR
#endif
#include "jpeglib.h"
#if defined (_MSC_VER)
#undef XMD_H
#undef JPEG_INTERNAL_OPTIONS
#endif

#include <png.h>

#include <windows.h>

using std::bad_alloc;
using std::cout;
using std::endl;
using std::string;
using std::vector;

// double VTTImage::timeForDeletion = 0.0;
// double VTTImage::timeForLib = 0.0;
// double VTTImage::timeForMalloc = 0.0;
// double VTTImage::timeForMemcpy = 0.0;
// UINT32 VTTImage::filesRead = 0;
// UINT32 VTTImage::filesInMemory = 0;

///////////////////////////////////////////////////////////////////////////////
// Local Functions Declarations

bool GetParametersOfJPEGFile( FILE *&in_file, UINT32 &width, UINT32 &height,
	UINT32 &numComponents );
bool GetParametersOfPNGFile( FILE *&in_file, UINT32 &width, UINT32 &height,
	UINT32 &numComponents );

bool LoadImageJPEG( const string &strFilePath, unsigned char *buffer,
	const UINT32 &x, const UINT32 &y,
	const UINT32 &width, const UINT32 &height );
bool LoadImagePNG( const string &strFilePath, unsigned char *buffer,
	const UINT32 &x, const UINT32 &y,
	const UINT32 &width, const UINT32 &height );

bool WriteImageJPEG( const string &fileName, const unsigned char *data,
	UINT32 width, UINT32 height, UINT32 numComponents, UINT32 quality );
bool WriteImagePNG( const string &fileName, const unsigned char *data,
	UINT32 width, UINT32 height, UINT32 numComponents );

///////////////////////////////////////////////////////////////////////////////

/**
  *	Constructs a new instance of VTTImage which is a Null-object
  */
VTTImage::VTTImage( void )
{
	//cout << "VTTImage Constructor called" << endl;
	InitMembers();
}

/**
  *	Constructs a new instance of VTTImage with an image file. The data of the
  * image is not loaded into memory unless Load() is called.
  *
  * @param strFilePath Path of the image file
  */
VTTImage::VTTImage( const string &strFilePath )
{
	mHasInternalData = false;
	Init( strFilePath );
}

/**
  *	Constructs a new instance of VTTImage with image data. The image therefore
  * does not belong to an image file.
  *
  * @param internalData Pointer to the image data
  * @param width Width of the image in pixels
  * @param height Height of the image in pixels
  * @param numComponents Number of the components in a single pixel
  */
VTTImage::VTTImage( const unsigned char *internalData,
	UINT32 width, UINT32 height, UINT32 numComponents )
{
	InitMembers();

	try
	{	
		mInternalData = new unsigned char[width * height * numComponents];
	}
	catch (bad_alloc)// &ba)
	{
		//cout << "ERROR: Not enough memory to construct image!" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTImage::VTTImage(const unsigned char*, UINT32, UINT32, UINT32): Not enough memory to construct image" );
		return;
	}

	mHeight = height;
	mWidth = width;

	mNumComponents = numComponents;

	mBytesOnTheHeap = width * height * numComponents;

	mCorrespondsToImageFile = false;
	mHasInternalData = true;
	mIsNull = false;

	memcpy( mInternalData, internalData, mBytesOnTheHeap );
}

/**
  *	Copy constructor. Constructs a new instance of VTTImage with an existing instance.
  *
  * @param image const reference to an existing image
  */
VTTImage::VTTImage( const VTTImage &image )
{
	mImagePath = image.mImagePath;

	for (vector<VTTPackNode*>::const_iterator it = image.mPackNodeVector.begin();
		it != image.mPackNodeVector.end(); it++)
	{
		mPackNodeVector.push_back( new VTTPackNode( *(*it) ) );
	}

	mBytesOnTheHeap = image.mBytesOnTheHeap;

	mHeight = image.mHeight;
	mNumComponents = image.mNumComponents;
	mWidth = image.mWidth;

	mColorSpace = image.mColorSpace;
	mUnderlyingImageFormat = image.mUnderlyingImageFormat;

	mCorrespondsToImageFile = image.mCorrespondsToImageFile;
	mHasInternalData = image.mHasInternalData;
	mIsNull = image.mIsNull;

	mLastChangingDate = image.mLastChangingDate;


	if (mHasInternalData)
	{
		try
		{	
			mInternalData =
				new unsigned char[mBytesOnTheHeap];
		}
		catch (bad_alloc)// &ba)
		{
			//cout << "ERROR: Not enough memory to copy internal VTTImage data"
			//	 << endl;
			VTTErrorManager::Get()->pushErrorMessage( "VTTImage::VTTImage(const VTTImage&): Not enough memory to copy image data" );
			mBytesOnTheHeap = 0;
			mHasInternalData = false;
			return;
		}

		memcpy( mInternalData, image.mInternalData, mBytesOnTheHeap );
	}
}

/**
  *	Initializes the member variables. The instance is a Null-object after that.
  */
void VTTImage::InitMembers( void )
{
	mImagePath = "";

	mInternalData = 0;
	mRowPointers = 0;
	//mPackNodeVector.clear();

	mHeight = 0;
	mNumComponents = 0;
	mWidth = 0;

	mCorrespondsToImageFile = false;
	mHasInternalData = false;
	mIsNull = true;

	mColorSpace = VTT_CS_NOT_SPECIFIED;
	mUnderlyingImageFormat = VTT_IMAGE_NOT_SPECIFIED;

	mLastChangingDate.tm_hour = -1;
	mLastChangingDate.tm_isdst = -1;
	mLastChangingDate.tm_mday = -1;
	mLastChangingDate.tm_min = -1;
	mLastChangingDate.tm_mon = -1;
	mLastChangingDate.tm_sec = -1;
	mLastChangingDate.tm_wday = -1;
	mLastChangingDate.tm_yday = -1;
	mLastChangingDate.tm_year = -1;
}

/**
  *	Destroys the instance and removes all the image data from memory.
  */
VTTImage::~VTTImage( void )
{
	//cout << "VTTImage Destructor called" << endl;
	//if (mPackNode) delete mPackNode;

	for (vector<VTTPackNode*>::const_iterator it = mPackNodeVector.begin();
		it != mPackNodeVector.end(); it++)
	{
		delete *it;
	}

	DeleteInternalData();
}

/**
  *	Removes all the image data from memory
  *
  * @return Number of bytes belonging to image data that were deleted
  */
UINT32 VTTImage::DeleteInternalData( void )
{
	if (HasInternalData())
	{
		UINT32 num_data = mBytesOnTheHeap;

		delete [] mInternalData;

		mBytesOnTheHeap = 0;
		mInternalData = 0;
		mHasInternalData = false;

		return num_data;
	}

	return 0;
}

/**
  *	Calls DeleteInternalData(). If the instance did not belong an image file
  * it is a Null-object after that.
  */
UINT32 VTTImage::FreeAllocatedMemory( void )
{
	UINT32 num_data = DeleteInternalData();

	if (!mCorrespondsToImageFile)
		mIsNull = true;

	return num_data;
}

/**
  *	Returns a const pointer to the image data.
  *
  * @return const pointer to the image data or 0 when there is no image data
  */
const unsigned char *VTTImage::GetDataPointer( void ) const
{
	return mInternalData;
}

/**
  *	Returns the height of the image in pixels
  *
  * @return height of the image in pixels
  */
UINT32 VTTImage::GetHeight( void ) const
{
	return mHeight;
}

/**
  *	Returns the full path of the image
  *
  * @return full path of the image
  */
string VTTImage::GetImagePath( void ) const
{
	return mImagePath;
}

/**
  *	Returns the time stamp that stores the information of the last changing date of the image
  *
  * @return time stamp that stores the information of the last changing date of the image
  */
const tm *VTTImage::GetLastChangingDate( void ) const
{
	if (!CorrespondsToImageFile())
		return 0;

	return &mLastChangingDate;
}

/**
  *	Returns the number of components used for a single pixel
  *
  * @return number of components used for a single pixel
  */
UINT32 VTTImage::GetNumComponents( void ) const
{
	return mNumComponents;
}

/**
  *	Returns the vector with all the pack nodes of the image.
  * A pack node stores the information about the location of the image inside the Atlas.
  *
  * @return vector with all the pack nodes of the image
  */
const vector<VTTPackNode*> &VTTImage::GetPackNodeVector( void ) const
{
	return mPackNodeVector;
}

/**
  *	Returns the width of the image in pixels
  *
  * @return width of the image in pixels
  */
UINT32 VTTImage::GetWidth( void ) const
{
	return mWidth;
}

/**
  *	Adds a packnode, which is a new occurence of the image inside the Atlas
  *
  * @param packNode pack node with the information of the location of the image inside the Atlas
  */
void VTTImage::AddPackNode( const VTTPackNode* packNode )
{
	if (!packNode) return;
	mPackNodeVector.push_back( new VTTPackNode( *packNode ) );
}

/**
  *	Clears the PackNode-vector, so that the image has no occurence in the Atlas
  */
void VTTImage::ClearPackNodes( void )
{
	for (vector<VTTPackNode*>::const_iterator it = mPackNodeVector.begin();
		it != mPackNodeVector.end(); it++)
	{
		delete *it;
	}

	mPackNodeVector.clear();
}

/**
  *	Returns a pointer to a new instance which is a subimage of this image. The
  * subimage is cropped when its dimensions exceed the image.
  * Delete the subimage when you don't need it anymore.
  *
  * @param x x-coordinate of the upper left pixel of the subimage inside this image
  * @param x y-coordinate of the upper left pixel of the subimage inside this image
  * @param width width of the subimage in pixels
  * @param height of the subimage in pixels
  * @return pointer to the new subimage
  */
VTTImage *VTTImage::GetSubImage( UINT32 x, UINT32 y,
	UINT32 width, UINT32 height ) const
{
	if (IsNull())
	{
		return 0;
	}

	if (CorrespondsToImageFile())
	{
		//filesRead++;

		unsigned char *sub_image =
			new unsigned char[mWidth * mHeight *mNumComponents];

		if (mUnderlyingImageFormat == VTT_IMAGE_JPEG)
		{
			if (!LoadImageJPEG( mImagePath, sub_image, x, y, width, height ))
			{
				delete [] sub_image;
				return 0;
			}
				
		} else if (mUnderlyingImageFormat == VTT_IMAGE_PNG)
		{
			if (!LoadImagePNG( mImagePath, sub_image, x, y, width, height ))
			{
				delete [] sub_image;
				return 0;
			}

		}

		/* yup, we succeeded! */
		VTTImage *vtt_image = new VTTImage( sub_image, width, height, mNumComponents );

		delete sub_image;

		return vtt_image;
	} else if (HasInternalData())
	{

		UINT32 current_index = (y * mWidth + x) * mNumComponents;
		UINT32 line_inc = mWidth * mNumComponents;

		UINT32 sub_image_index = 0;
		UINT32 sub_image_inc = width * mNumComponents;
		unsigned char *sub_image =
			new unsigned char[width * height * mNumComponents];
		VTTImage *image;

		for (UINT32 i = 0; i < height; i++)
		{
			memcpy( &sub_image[sub_image_index], &mInternalData[current_index],
				sub_image_inc );
			
			current_index += line_inc;
			sub_image_index += sub_image_inc;
		}

		image = new VTTImage( sub_image, width, height, mNumComponents );

		delete [] sub_image;

		return image;
	} 
		
	return 0;
}

/**
  *	Initializes the image object with all the information of the image file
  * corresponding to the passed image file path. No image data is loaded.
  *
  * @param strFilePath path to the image file
  * @return boolean value indicating whether the initialization procedure was successful or not
  */
bool VTTImage::Init( const string &strFilePath )
{
	DeleteInternalData();
	InitMembers();

	if (IsValidImageName( strFilePath ))
	{
		if (IsFile( strFilePath ))
		{
			FILE *infile;
			string image_ext = GetFileExtension( strFilePath );

			fopen_s( &infile, strFilePath.c_str(), "rb" );

			if (!infile)
			{
				//printf( "Error opening image file %s\n!", strFilePath );
				//cout << "ERROR: Could not open image file " << strFilePath << endl;
				VTTErrorManager::Get()->pushErrorMessage( "VTTImage::Init(const std::string&): Could not open input image file " + strFilePath + " [ERRNO: " + UINT32ToString( errno ) + "]" );
				return false;
			}

			if (image_ext == "jpg" || image_ext == "jpeg")
			{
				GetParametersOfJPEGFile( infile, mWidth, mHeight, mNumComponents );
				mUnderlyingImageFormat = VTT_IMAGE_JPEG;
			} else if (image_ext == "png")
			{
				GetParametersOfPNGFile( infile, mWidth, mHeight, mNumComponents );
				mUnderlyingImageFormat = VTT_IMAGE_PNG;
			}

			fclose( infile );

			mIsNull = false;
			mCorrespondsToImageFile = true;
			mImagePath = strFilePath;

			mLastChangingDate = GetFileChangingDate( strFilePath );

			return true;
		} else
		{
			VTTErrorManager::Get()->pushErrorMessage( "VTTImage::Init(const std::string&): Input image file " + strFilePath + " does not exist" );
		}
	} else
	{
		VTTErrorManager::Get()->pushErrorMessage( "VTTImage::Init(const std::string&): " + GetFileNameFromFullPath( strFilePath ) + " is an invalid image name" );
	}

	//cout << "ERROR: Could not open image " << strFilePath << endl;

	return false;
}

/**
  *	Loads the image data of the corresponding image file into memory. The loaded
  * data is then registered in the MemoryManager.
  *
  * @return boolean value indicating whether the loading procedure was successful or not
  */
bool VTTImage::Load( void )
{
	if (!mCorrespondsToImageFile)
		return false;

	if (mHasInternalData)
		DeleteInternalData();

	//mWidth = cinfo.output_width;
	//mHeight = cinfo.output_height;
	//mNumComponents = cinfo.num_components;
	
	bool internal_data_is_allocated = false;

	if (!(mLastChangingDate == GetFileChangingDate( mImagePath )))
		if (!Init( mImagePath ))
			return false;

	mBytesOnTheHeap = mWidth * mHeight * mNumComponents;

	try
	{	
		mInternalData = new unsigned char[mBytesOnTheHeap];
		internal_data_is_allocated = true;
	}
	catch (bad_alloc)// &ba)
	{
		//cout << endl << "WARNING: Memory allocation error: " << ba.what()
		//	 << endl;
	}

	if (!internal_data_is_allocated)
	{
		// There is not enough memory, try to free some
		bool result =
			VTTMemoryManager::Get()->RequestMemory( mBytesOnTheHeap );

		if (!result)
		{
			//cout << "ERROR: Not enough memory to load image" << endl;
			VTTErrorManager::Get()->pushErrorMessage( "VTTImage::Load(): Not enough memory to load image" );
			mBytesOnTheHeap = 0;

			return false;
		} else
		{
			mInternalData = new unsigned char[mBytesOnTheHeap];
			mHasInternalData = true;
		}
	}

	VTTMemoryManager::Get()->Register( this, mBytesOnTheHeap );

	/* now actually read the image file into the buffer */

	if (mUnderlyingImageFormat == VTT_IMAGE_JPEG)
	{
		if (!LoadImageJPEG( mImagePath, mInternalData, 0, 0, mWidth, mHeight ))
		{
			delete [] mInternalData;
			mHasInternalData = false;
			return false;
		}
	} else if (mUnderlyingImageFormat == VTT_IMAGE_PNG)
	{
		if (!LoadImagePNG( mImagePath, mInternalData, 0, 0, mWidth, mHeight ))
		{
			delete [] mInternalData ;
			mHasInternalData = false;
			return false;
		}
	}

	mHasInternalData = true;
	//filesRead++;
	
	/* yup, we succeeded! */
	return true;
}

/**
  *	Reloads the image (for example when it has changed)
  *
  * @return boolean value indicating whether the reloading procedure was successful or not
  */
bool VTTImage::Reload( void )
{
	if (!mCorrespondsToImageFile)
		return false;

	if (!mHasInternalData)
		return Load();

	if (mLastChangingDate == GetFileChangingDate( mImagePath ))
		return true;

	if (mUnderlyingImageFormat == VTT_IMAGE_JPEG)
	{
		if (!LoadImageJPEG( mImagePath, mInternalData, 0, 0, mWidth, mHeight ))
		{
			//delete [] mInternalData;
			//mHasInternalData = false;
			return false;
		}
	} else if (mUnderlyingImageFormat == VTT_IMAGE_PNG)
	{
		if (!LoadImagePNG( mImagePath, mInternalData, 0, 0, mWidth, mHeight ))
		{
			//delete [] mInternalData ;
			//mHasInternalData = false;
			return false;
		}
	}

	mLastChangingDate = GetFileChangingDate( mImagePath );

	//filesRead++;

	/* yup, we succeeded! */
	return true;
}

/**
  *	Returns a boolean value indicating whether the image corresponds to image file or not
  *
  * @return boolean value indicating whether the image corresponds to image file or not
  */
bool VTTImage::CorrespondsToImageFile( void ) const
{
	return mCorrespondsToImageFile;
}

/**
  *	Updates the last changing date, which is the time stamp with the information of
  * the last change of the image.
  *
  * @return time stamp with the information of the last change of the image
  */
const tm *VTTImage::UpdateLastChangingDate( void )
{
	if (!CorrespondsToImageFile())
		return 0;

	mLastChangingDate = GetFileChangingDate( mImagePath );

	return &mLastChangingDate;
}

/**
  *	Stores the number of pixels in the image that corresponds to the
  * path in the numPixels argument.
  *
  * @param imagePath path of the image
  * @param numPixels number of pixels in the image
  * @return boolean value indicating whether the extraction of the information succeeded or not
  */
bool VTTImage::GetNumPixels( const string &imagePath, UINT32 &numPixels )
{
	UINT32 width;
	UINT32 height;

	if (GetSize( imagePath, width, height ))
	{
		numPixels = width * height;
		return true;
	} else
	{
		numPixels = 0;
		return false;
	}
}

/**
  *	Stores the width and height in pixels of the image that corresponds to the
  * path in the width and height arguments.
  *
  * @param imagePath path of the image
  * @param width width of the image in pixels
  * @param height height of the image in pixels
  * @return boolean value indicating whether the extraction of the information succeeded or not
  */
bool VTTImage::GetSize( const string &imagePath, UINT32 &width, UINT32 &height )
{
	if (!IsValidImageName( imagePath ))
	{
		//cout << "ERROR: Could not open image " << imagePath << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTImage::GetSize(const string &, UINT32, UINT32): " + GetFileNameFromFullPath( imagePath ) + " is an invalid image name" );
		width = 0;
		height = 0;
		return false;
	}

	if (!IsFile( imagePath ))
	{
		//cout << "ERROR: Could not open image " << imagePath << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTImage::GetSize(const string &, UINT32, UINT32): Input image file " + imagePath + " does not exist" );
		width = 0;
		height = 0;
		return false;
	}

	bool all_ok = true;
	UINT32 num_components;
	string image_ext = GetFileExtension( imagePath );

	FILE *in_file = 0;
	fopen_s( &in_file, imagePath.c_str(), "rb" );

	if (!in_file)
	{
		//cout << "ERROR: Could not open image " << imagePath << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTImage::GetSize(const string &, UINT32, UINT32): Could not open input image file " + imagePath + " [ERRNO: " + UINT32ToString( errno ) + "]" );
		width = 0;
		height = 0;
		return false;
	}

	if (image_ext == "jpg" || image_ext == "jpeg")
	{
		all_ok = GetParametersOfJPEGFile( in_file, width, height, num_components );
	} else if (image_ext == "png")
	{
		all_ok = GetParametersOfPNGFile( in_file, width, height, num_components );
	} else
	{
		all_ok = false;
	}

	fclose( in_file );

	if (!all_ok)
		VTTErrorManager::Get()->pushErrorMessage( "VTTImage::GetSize(const string &, UINT32, UINT32): Could not read parameters from input image " + imagePath );

	return all_ok;
}

/**
  *	Writes an image file (Jpeg or PNG) to disc. Therefore the exact path, the raw image data,
  * the width and height, the number of components in a single pixel and for Jpeg-files
  * the quality parameter have to be provided.
  *
  * @param fileName full path of the image that is written do disc
  * @param data raw image data
  * @param width width of the image in pixels
  * @param height height of the image in pixels
  * @param numComponents number of components in a single pixel
  * @param quality quality parameter for Jpeg-files, must be a value in [0..100], default is 50
  * @return boolean value indicating whether the image generation was successful or not
  */
bool VTTImage::WriteImageFile( const string &fileName, const unsigned char *data,
	UINT32 width, UINT32 height, UINT32 numComponents, UINT32 quality )
{
	bool result;

	if (StringEndsWith( fileName, ".jpg" ))
	{
		result = WriteImageJPEG( fileName, data, width, height, numComponents, quality );

		if (!result)
			VTTErrorManager::Get()->pushErrorMessage( "VTTImage::WriteImageFile(const string&, const unsigned char*, UINT32, UINT32, UINT32, UINT32): Could not write output image " + fileName );

		return result;

	}
	else if (StringEndsWith( fileName, ".png" ))
	{
		result = WriteImagePNG( fileName, data, width, height, numComponents );

		if (!result)
			VTTErrorManager::Get()->pushErrorMessage( "VTTImage::WriteImageFile(const string&, const unsigned char*, UINT32, UINT32, UINT32, UINT32): Could not write output image " + fileName );

		return result;
	}

	//cout << "ERROR: Unsupported Image format " << GetFileExtension( fileName )
	//	 << endl;

	VTTErrorManager::Get()->pushErrorMessage( "VTTImage::WriteImageFile(const string&, const unsigned char*, UINT32, UINT32, UINT32, UINT32): Unsupported Image format " + GetFileExtension( fileName ) );

	return false;
}


///////////////////////////////////////////////////////////////////////////////
// LOCAL FUNCTIONS
///////////////////////////////////////////////////////////////////////////////

/**
  *	Stores the parameters of a Jpeg-file into arguments, which are therefore
  * passed by reference. The width, height and number of components per pixel are
  * evaluated.
  *
  * @param in_file file object that corresponds to the image file
  * @param width reference to an UINT32 variable to write the width of the image into
  * @param height reference to an UINT32 variable to write the height of the image into
  * @param numComponents reference to an UINT32 variable to write the number of components per pixel into
  * @return boolean value indicating whether the evaluation of the information was successful or not
  */
bool GetParametersOfJPEGFile( FILE *&in_file, UINT32 &width, UINT32 &height, UINT32 &numComponents )
{
	jpeg_decompress_struct c_info;
	jpeg_error_mgr jerr;

	/* here we set up the standard libjpeg error handler */
	c_info.err = jpeg_std_error( &jerr );
	/* setup decompression process and source, then read JPEG header */
	jpeg_create_decompress( &c_info );
	// this makes the library read from in_file
	jpeg_stdio_src( &c_info, in_file );
	// reading the image header which contains image information
	jpeg_read_header( &c_info, TRUE );

	width = c_info.image_width;
	height = c_info.image_height;
	numComponents = c_info.num_components;

	jpeg_destroy_decompress( &c_info );

	return true;
}

/**
  *	Stores the parameters of a PNG-file into arguments, which are therefore
  * passed by reference. The width, height and number of components per pixel are
  * evaluated.
  *
  * @param in_file file object that corresponds to the image file
  * @param width reference to an UINT32 variable to write the width of the image into
  * @param height reference to an UINT32 variable to write the height of the image into
  * @param numComponents reference to an UINT32 variable to write the number of components per pixel into
  * @return boolean value indicating whether the evaluation of the information was successful or not
  */
bool GetParametersOfPNGFile( FILE *&in_file, UINT32 &width, UINT32 &height,
	UINT32 &numComponents )
{
	bool is_png = false;
	unsigned char header[8];

	fread( header, 1, 8, in_file );

	//cout << "PNG Header: " << header << endl;
	
	is_png = !png_sig_cmp(header, 0, 8);

	if (!is_png)
	{
		return false;
	}

	png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
		0, 0, 0 );
	
	if (!png_ptr)
		return false;
	
	png_infop info_ptr = png_create_info_struct( png_ptr );
	
	if (!info_ptr)
	{
		png_destroy_read_struct( &png_ptr, 0, 0 );
		return false;
	}

	/*png_infop end_info = png_create_info_struct( png_ptr );

	if (!end_info)
	{
		png_destroy_read_struct( &png_ptr, &info_ptr, 0 );
		
		return false;
	}*/

	if (setjmp( png_jmpbuf( png_ptr ) ))
		return false;

	png_init_io( png_ptr, in_file );

	png_set_sig_bytes( png_ptr, 8 );

	png_read_info( png_ptr, info_ptr );

	width = png_get_image_width( png_ptr,info_ptr );
	height = png_get_image_height( png_ptr,info_ptr );
	numComponents = png_get_channels( png_ptr, info_ptr );

	png_destroy_read_struct( &png_ptr, &info_ptr, 0 );

	/*int bit_depth;
	int color_type;
	int interlace;
	int compression_type;
	int filter_method;

	png_get_IHDR( png_ptr, info_ptr, &width, &height,
		&bit_depth, &color_type, &interlace,
		&compression_type, &filter_method );*/

	return true;
}

/**
  *	Loads a PNG image file. The image data is therefore stored in the provided
  * buffer. The exact region that should be loaded has to be defined via
  * the x, y, width and height arguments. If the provided subimage region exceeds
  * the image region, no data is read.
  *
  * @param strFilePath full path of the PNG image file
  * @param buffer buffer to store the raw image data into
  * @param x x-coordinate of the upper left pixel of the subimage that should be loaded
  * @param y y-coordinate of the upper left pixel of the subimage that should be loaded
  * @param width width of the desired subimage in pixels
  * @param height height of the desired subimage in pixels
  * @return boolean value indicating whether the loading procedure was successful or not
  */
bool LoadImagePNG( const string &strFilePath, unsigned char *buffer,
	const UINT32 &x, const UINT32 &y,
	const UINT32 &width, const UINT32 &height )
{
	FILE *in_file;
	fopen_s( &in_file, strFilePath.c_str(), "rb" );

	if (!in_file)
	{
		//cout << errno << endl;
		//printf( "Error opening PNG file %s\n!", strFilePath );
		//cout << "ERROR: Could not open PNG file " << strFilePath << endl;
		return false;
	}

	bool is_png = false;
	unsigned char header[8];

	fread( header, 1, 8, in_file );

	//cout << "PNG Header: " << header << endl;
	
	is_png = !png_sig_cmp(header, 0, 8);

	if (!is_png)
	{
		fclose( in_file );
		return false;
	}

	png_structp png_ptr =
		png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
	
	if (!png_ptr)
	{
		fclose( in_file );
		return false;
	}
	
	png_infop info_ptr = png_create_info_struct( png_ptr );
	
	if (!info_ptr)
	{
		fclose( in_file );
		png_destroy_read_struct( &png_ptr, 0, 0 );
		return false;
	}

	/*png_infop end_info = png_create_info_struct( png_ptr );

	if (!end_info)
	{
		png_destroy_read_struct( &png_ptr, &info_ptr, 0 );
		
		return false;
	}*/

	if (setjmp( png_jmpbuf( png_ptr ) ))
	{
		fclose( in_file );
		return false;
	}

	png_init_io( png_ptr, in_file );

	png_set_sig_bytes( png_ptr, 8 );

	png_read_info( png_ptr, info_ptr );

	UINT32 real_width = png_get_image_width( png_ptr,info_ptr );
	UINT32 real_height = png_get_image_height( png_ptr,info_ptr );
	UINT32 num_components = png_get_channels( png_ptr, info_ptr );

	if (x < 0 || x >= real_width || y < 0 || y >= real_height || width <= 0 ||
		height <= 0 || x + width - 1 >= real_width ||
		y + height - 1 >= real_height)
	{
		png_destroy_read_struct( &png_ptr, &info_ptr, 0 );
		fclose( in_file );
		return false;
	}

	// Everything is OK, load the file
	/*cout << "Width: " << width << endl;
	cout << "Height: " << height << endl;
	cout << "sizeof(png_bytep): " << sizeof(png_bytep) << endl;
	cout << "png_get_rowbytes(png_ptr,info_ptr): " << png_get_rowbytes(png_ptr,info_ptr)  << endl;*/
	
	png_bytepp row_pointers =
		static_cast<png_bytepp>( malloc( sizeof(png_bytep) * height ) );

	UINT32 buffer_index = 0;
	UINT32 buffer_inc = width * num_components;
	for (UINT32 i = 0; i < height; i++)
	{
		row_pointers[i] = &buffer[buffer_index];

		buffer_index += buffer_inc;
	}

	png_read_image( png_ptr, row_pointers );

	png_destroy_read_struct( &png_ptr, &info_ptr, 0 );

	int bit_depth;
	int color_type;
	int interlace;
	int compression_type;
	int filter_method;
	UINT32 local_width = width;
	UINT32 local_height = height;

	png_get_IHDR( png_ptr, info_ptr, &local_width, &local_height,
		&bit_depth, &color_type, &interlace,
		&compression_type, &filter_method );

	fclose( in_file );

	return true;
}

/**
  *	Loads a Jpeg image file. The image data is therefore stored in the provided
  * buffer. The exact region that should be loaded has to be defined via
  * the x, y, width and height arguments. If the provided subimage region exceeds
  * the image region, no data is read.
  *
  * @param strFilePath full path of the Jpeg image file
  * @param buffer buffer to store the raw image data into
  * @param x x-coordinate of the upper left pixel of the subimage that should be loaded
  * @param y y-coordinate of the upper left pixel of the subimage that should be loaded
  * @param width width of the desired subimage in pixels
  * @param height height of the desired subimage in pixels
  * @return boolean value indicating whether the loading procedure was successful or not
  */
bool LoadImageJPEG( const string &strFilePath, unsigned char *buffer,
	const UINT32 &x, const UINT32 &y,
	const UINT32 &width, const UINT32 &height )
{
	FILE *in_file;
	fopen_s( &in_file, strFilePath.c_str(), "rb" );

	if (!in_file)
	{
		//printf( "Error opening jpeg file %s\n!", strFilePath );
		//cout << "ERROR: Could not open JPEG file " << strFilePath << endl;
		return false;
	}

	/* these are standard libjpeg structures for reading(decompression) */
	jpeg_decompress_struct cinfo;
	jpeg_error_mgr jerr;
	/* libjpeg data structure for storing one row, that is, scanline of an image */
	JSAMPROW row_pointer[1];

	/* here we set up the standard libjpeg error handler */
	cinfo.err = jpeg_std_error( &jerr );
	/* setup decompression process and source, then read JPEG header */
	jpeg_create_decompress( &cinfo );
	/* this makes the library read from in_file */
	jpeg_stdio_src( &cinfo, in_file );
	/* reading the image header which contains image information */
	jpeg_read_header( &cinfo, TRUE );

	UINT32 real_width = cinfo.image_width;
	UINT32 real_height = cinfo.image_height;

	if (x < 0 || x >= real_width || y < 0 || y >= real_height || width <= 0 ||
		height <= 0 || x + width - 1 >= real_width ||
		y + height - 1 >= real_height)
	{
		fclose( in_file );
		return false;
	}

	/* Start decompression jpeg here */
	jpeg_start_decompress( &cinfo );

	row_pointer[0] = (unsigned char *)malloc( width * cinfo.num_components );

	if (!row_pointer[0])
	{
		VTTMemoryManager::Get()->RequestMemory( width * cinfo.num_components );
		
		row_pointer[0] =
			(unsigned char *)malloc( width * cinfo.num_components );
	}

	if (!row_pointer[0])
	{
		//cout << "ERROR: Not enough memory to load image file" << endl;
		return false;
	}

	UINT32 current_y = 0;
	UINT32 row_start_index = x * cinfo.num_components;
	UINT32 sub_image_index = 0;
	UINT32 sub_image_inc = width * cinfo.num_components;

	/* read one scan line at a time */
	while (cinfo.output_scanline < cinfo.image_height)
	{
		jpeg_read_scanlines( &cinfo, row_pointer, 1 );

		if (current_y >= y && current_y < y + height)
		{
			memcpy( &buffer[sub_image_index], &row_pointer[0][row_start_index],
				sub_image_inc );

			sub_image_index += sub_image_inc;
		}

		current_y++;
	}

	/*	wrap up decompression, destroy objects,
	 *	free pointers and close open files
	 */

	jpeg_finish_decompress( &cinfo );
	jpeg_destroy_decompress( &cinfo );
	free( row_pointer[0] );
	fclose( in_file );

	return true;
}

/**
  *	Writes a Jpeg image file to disc. Therefore the exact path, the raw image data,
  * the width and height, the number of components in a single pixel and
  * the quality parameter have to be provided.
  *
  * @param fileName full path of the image that is written do disc
  * @param data raw image data
  * @param width width of the image in pixels
  * @param height height of the image in pixels
  * @param numComponents number of components in a single pixel
  * @param quality quality parameter, must be a value in [0..100]
  * @return boolean value indicating whether the image generation was successful or not
  */
bool WriteImageJPEG( const string &fileName, const unsigned char *data,
	UINT32 width, UINT32 height, UINT32 numComponents, UINT32 quality )
{
	FILE *outfile;
	fopen_s( &outfile, fileName.c_str(), "wb" );

	if ( !outfile )
	{
		//printf("Error opening output jpeg file %s\n!", fileName );
		//cout << "ERROR: Could not open JPEG output file " << fileName << endl;
		//cout << "ERROR-NR: " << errno << endl;
		return false;
	}

	jpeg_compress_struct cinfo;
	jpeg_error_mgr jerr;

	UINT32 scan_line_size = width * numComponents;
	unsigned char *scan_line = static_cast<unsigned char*>( 
		malloc( scan_line_size ) );

	/* this is a pointer to one row of image data */
	JSAMPROW row_pointer[1];
	cinfo.err = jpeg_std_error( &jerr );
	jpeg_create_compress(&cinfo);
	jpeg_stdio_dest(&cinfo, outfile);

	/* Setting the parameters of the output file here */
	cinfo.image_width = width;
	cinfo.image_height = height;
	cinfo.input_components = numComponents;
	cinfo.in_color_space = JCS_RGB;

	//cout << "Write: " << JCS_RGB << endl;

	/* default compression parameters, we shouldn't be worried about these */
	jpeg_set_defaults( &cinfo );

	// set quality
	jpeg_set_quality( &cinfo, quality, false );

	/*cinfo.write_Adobe_marker = global_decompress_info.saw_Adobe_marker;
	cinfo.Se = global_decompress_info.Se;
	cinfo.write_JFIF_header = global_decompress_info.saw_JFIF_marker;*/

	/* Now do the compression .. */
	jpeg_start_compress( &cinfo, TRUE );

	/* like reading a file, this time write one row at a time */
	while( cinfo.next_scanline < cinfo.image_height )
	{
		memcpy( scan_line,
			&data[cinfo.next_scanline *
				cinfo.image_width * cinfo.input_components],
			scan_line_size );
		row_pointer[0] = scan_line;
		jpeg_write_scanlines( &cinfo, row_pointer, 1 );
	}
	/* similar to read file, clean up after we're done compressing */
	jpeg_finish_compress( &cinfo );
	jpeg_destroy_compress( &cinfo );

	free( scan_line );
	fclose( outfile );
	/* success code is 1! */
	return true;
}

/**
  *	Writes a PNG image file to disc. Therefore the exact path, the raw image data,
  * the width and height, and the number of components in a single pixel have to be provided.
  *
  * @param fileName full path of the image that is written do disc
  * @param data raw image data
  * @param width width of the image in pixels
  * @param height height of the image in pixels
  * @param numComponents number of components in a single pixel
  * @return boolean value indicating whether the image generation was successful or not
  */
bool WriteImagePNG( const string &fileName, const unsigned char *data,
	UINT32 width, UINT32 height, UINT32 numComponents )
{
	/* create file */
	FILE *fp;

	fopen_s( &fp, fileName.c_str(), "wb" );

	if (!fp)
	{
		//printf("ERROR: Could not open output PNG file %s\n!", fileName );
		//cout << "ERROR: Could not open PNG output file " << fileName << endl;
		return false;
	}

	/* initialize stuff */
	png_structp png_ptr =
		png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );

	if (!png_ptr)
	{
		fclose( fp );
		return false;
	}

	png_infop info_ptr = png_create_info_struct(png_ptr);

	if (!info_ptr)
	{
		fclose( fp );
		png_destroy_read_struct( &png_ptr, 0, 0 );
		return false;
	}

	if (setjmp( png_jmpbuf( png_ptr ) ))
	{
		fclose( fp );
		png_destroy_read_struct( &png_ptr, &info_ptr, 0 );
		return false;
	}

	png_init_io( png_ptr, fp );


	/* write header */
	if (setjmp( png_jmpbuf( png_ptr ) ))
	{
		fclose( fp );
		png_destroy_read_struct( &png_ptr, &info_ptr, 0 );
		return false;
	}

	png_set_IHDR( png_ptr, info_ptr, width, height,
		8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
		PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );

	png_write_info( png_ptr, info_ptr );

	/* write bytes */
	if (setjmp( png_jmpbuf( png_ptr ) ))
	{
		fclose( fp );
		png_destroy_read_struct( &png_ptr, &info_ptr, 0 );
		return false;
	}

	unsigned char **row_pointers =
		static_cast<png_bytepp>( malloc( sizeof(png_bytep) * height ) );


	UINT32 image_size = width * height * numComponents;
	unsigned char *image = static_cast<unsigned char*>( 
		malloc( image_size ) );

	UINT32 buffer_index = 0;
	UINT32 buffer_inc = width * numComponents;

	for (UINT32 i = 0; i < height; i++)
	{
		row_pointers[i] = &image[buffer_index];

		buffer_index += buffer_inc;
	}

	memcpy( image, data, image_size );

	png_write_image( png_ptr, row_pointers );


	/* end write */
	if (setjmp( png_jmpbuf( png_ptr ) ))
	{
		fclose( fp );
		png_destroy_read_struct( &png_ptr, &info_ptr, 0 );
		return false;
	}

	png_write_end( png_ptr, 0 );

	/* cleanup heap allocation */
	free( row_pointers );
	free( image );

	fclose(fp);

	return true;
}